package com.kusen.cas;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 资源类
 */
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
class User {
    private String name;
    private Integer age;
}

/**
 * java内存模型的特点：一致性、原子性、可见性
 * cas：比较并交换(先获取，再获取，再比较。相同值+1)
 * 线程在工作的时候，会将主内存中的数据获取到自己的工作内存中，并且带着有修改的数据。最后拿着工作内存中的数据将数据和主内中的数据进行比较
 * 如果工作内存中的数据和主内存中的数据是一致的，就间数据修改到内存中去。反之则修改失败
 * 为什么cas不加锁也可以是保证数据的原子性：
 * cas底层是一个cpu的原语。也就是相当于一个汇编指令，所有的操作都是连续的，所以不会发生数据一致性问题
 * 缺点：
 * 1.只有一个的时候，只有一个共享变量的时候是能保证其原子性。但是有多个共享变量是是不能保证原则性
 * 2.当循环比较不成功的时候，就会一直比较下去，cpu开销会很大
 * 3.会产生ABA问题
 * ABA问题的产生：两个线程存在着时间差，那么有可能存在这个时间短的那个线程会将内存中的数据发生多次的改变并最后一次修改
 * 和刚刚开始的值是一样的。这时时间慢的这个线程去修改的时候发现内存中的数据还是一样的，就修改成功。但是
 * 在这之前其实内存中的值已经发生了很多次的改变。
 * 解决ABA问题：使用版本控制来判断当前是否发生的改变
 */

public class CasDome {
    public static void main(String[] args) {
        //基本数据类型的原子操作
        //atomicIntegerDome();
        //atomicReferenceDome();

    }

//    /**
//     * 用户之定义原子操作
//     */
//    private static void atomicReferenceDome() {
//        //原子引用操作,这是用户可以自定义的来操作原子引用
//        User zs=new User("zs",18);
//        User lisi=new User("lisi",20);
//        AtomicReference<User> atomicReference=new AtomicReference<>();
//        atomicReference.set(zs);
//        System.out.println(atomicReference.compareAndSet(zs, lisi)+"当前内存中的对象为："+atomicReference.get());
//        System.out.println(atomicReference.compareAndSet(zs, lisi)+"当前内存中的对象为："+atomicReference.get());
//    }

    /**
     * cas 基本数据类型原子操作
     */
    private static void atomicIntegerDome() {
        //cas锁使用
        AtomicInteger atomicInteger = new AtomicInteger(5);
        /**
         * 一个是期望值，一个是更新值，但期望值和原来的值相同时，才能够更改
         * 假设三秒前，我拿的是5，也就是expect为5，然后我需要更新成 2019
         *
         *   public final boolean compareAndSet(int expect, int update) {
         *         return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
         *     }
         *
         *     输出结果为：2019 2019
         *     解释：第一次比较的值是5，初始化的时候也是5 所以，目标值和初始值是一致的。这是发生了更改
         *           但是第二次就内存中的数据已经发生了改变，所以目标值和初始值不相等，所以输出了没有更改的内存中的数据
         */
        System.out.println(atomicInteger.compareAndSet(5, 2019) + "\t current data: " + atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(5, 1024) + "\t current data: " + atomicInteger.get());
        System.out.println(atomicInteger.getAndIncrement());
//        System.out.println(atomicInteger.getAndIncrement());
    }

}
